package com.terrynoya.fight.stateMachine
{
	import com.terrynoya.coreLib.MHashMap;
	import com.terrynoya.fight.char.Character;
	import com.terrynoya.fight.char.Entity;
	import com.terrynoya.fight.emuns.MoveType;
	import com.terrynoya.fight.emuns.PhysicsEnum;
	import com.terrynoya.fight.emuns.PlayerControl;
	import com.terrynoya.fight.emuns.StateTypeEnum;
	import com.terrynoya.fight.stateMachine.controllers.ChangeState;
	import com.terrynoya.fight.stateMachine.controllers.SelfState;

	/**
	 * 状态机
	 * @author yaojianzhou
	 */
	public class StateMachine
	{
		private var _states:MHashMap;
		
		private var _previouseState:State;
		
		private var _currentState:State;
		
		private var _stateTime:int;
		
		private var _character:Character;
		
		private var _foreignMachine:StateMachine;
		
		private var _persistanceMap:MHashMap;
		/**
		 * 
		 */
		public function StateMachine(character:Character)
		{
			this._character = character;
			this._states = new MHashMap();	
			this._persistanceMap = new MHashMap();
		}
		
		public function get previouseState():State
		{
			return _previouseState;
		}

		public function get character():Character
		{
			return _character;
		}

		public function get foreignMachine():StateMachine
		{
			return _foreignMachine;
		}

		public function set foreignMachine(value:StateMachine):void
		{
			_foreignMachine = value;
		}

		public function get states():MHashMap
		{
			return _states;
		}

		public function set states(value:MHashMap):void
		{
			_states = value;
		}

		public function get stateTime():int
		{
			return _stateTime;
		}

		public function get currentState():State
		{
			return _currentState;
		}

		public function run(hitPause:Boolean):void
		{
			//check landing
			this.runStateById(-2,hitPause,true);
			//check jump walkfwd run etc
			this.runStateById(-1,hitPause,true);
			this.runCurrentStateLoop(hitPause);
			this._stateTime ++;
		}
		
		/**
		 * 
		 * @param key
		 * @param value
		 */
		public function addState(key:int,value:State):void
		{
			this._states.put(key,value);
		}
		
		/**
		 * 
		 * @param key
		 * @return 
		 */
		public function getState(key:int,forceLocal:Boolean):State
		{
			if(this.foreignMachine != null && forceLocal == false)
			{
				return this.foreignMachine.getState(key,true);
			}
			if(!this._states.containsKey(key))
			{
				var state:State = new State(key);
				this._states.put(state.id,state);
			}
			return this._states.getValue(key);
		}
		
		/**
		 * 
		 * @param stateId
		 */
		public function changeState(stateId:int):void
		{
			if(!this._states.containsKey(stateId))
			{
				trace("state not exist:",stateId);
				return;
			}
			
			this._previouseState = this._currentState;
			
			trace(this._character.id," changeState:",stateId);
			this._currentState = this.getState(stateId,false);
			this._stateTime = -1;
		}
		
		public function runCurrentStateLoop(hitpause:Boolean):void
		{
			while(true)
			{
				if(this._stateTime == -1)
				{
					this._stateTime = 0;
					this.applyState(this.currentState);
				}
				if(this.runState(this.currentState,hitpause) == false)
				{
					break;
				}
			}
		}
		
		/**
		 * 
		 * @param id
		 * @param hitPause
		 */
		private function runStateById(id:int,hitPause:Boolean,forceLocal:Boolean):void
		{
			var state:State = this.getState(id,forceLocal);
			this.runState(state,hitPause);
		}
		
		private function runState(state:State,hitPause:Boolean):Boolean
		{
			if(state == null)
			{
				return false;
			}
			for (var i:int = 0; i < state.controllers.length; i++) 
			{
				var ctrl:StateController = state.controllers[i];
				if(hitPause == true && ctrl.ignoreHitPause == false)
				{
					continue;
				}
				
				var persistenceCheck:Boolean = state.id < 0 || this.persistenceCheck(ctrl); 
				if(persistenceCheck == false)
				{
					continue;
				}
				
				var check:Boolean = ctrl.triggers.Trigger(this._character);
//debugStart				
				if(check)
				{
					trace(this._character.id,"ctrl triggered:",ctrl.name,":",check);
					ctrl.triggers.Trigger(this._character);
				}
//debugEnd
				if(check == false)
				{
					continue;
				}
				
				if(ctrl.persistence == 0 || ctrl.persistence > 1)
				{
					this._persistanceMap.put(ctrl,ctrl.persistence);
				}
				
				ctrl.run(this._character);
				
				/**
				 *如果触发的是changeState,剩下的trigger不执行
				 * 因为当前state已经不是循环的那个state了 
				 * */
				if(ctrl is ChangeState || ctrl is SelfState)
				{
					return true;
				}
			}
			return false;
		}
		
		private function persistenceCheck(ctrl:StateController):Boolean
		{
			if(!this._persistanceMap.containsKey(ctrl))
			{
				return true;
			}
			var persistance:int = ctrl.persistence;
			if(persistance == 0)
			{
				return false;
			}
			else if(persistance == 1)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		
		private function applyState(state:State):void
		{
//			trace("apply state",state.id);
			if(state == null)
			{
				return;
			}
			this._persistanceMap.clear();
			
			if(state.animateIdSetted)
			{
				this._character.setLocalAnimation(state.animateId,0);
			}
			if(state.physics != PhysicsEnum.Unchanged)
			{
				this._character.physics = state.physics;
			}
			if(state.type != StateTypeEnum.Unchanged)
			{
				this._character.state = state.type;
			}
			if(state.moveType != MoveType.Unchanged)
			{
				this._character.moveType = state.moveType;
			}
			if(state.playerCtrlSetted)
			{
				this._character.playerControl = state.playerControl == 1 ? PlayerControl.InControl : PlayerControl.NoControl;
			}
			if(state.isVelSetted)
			{
				this._character.velocity = state.velSet;
			}
			if(state.hitdefPersistance == false)
			{
				this._character.offensiveInfo.activeHitDef = false;
				this._character.offensiveInfo.hitPauseTime = 0;
			}
			if(state.moveHitPersistance == false)
			{
				this._character.offensiveInfo.moveContact = 0;
				this._character.offensiveInfo.moveHit = 0;
			}
		}
		
		public function clone(chara:Character):StateMachine
		{
			var cloned:StateMachine = new StateMachine(chara);
			cloned.states = states;
			return cloned;
		}
		
		private function runStateControls():void
		{
			
		}
	}
}